<template>
  <transition name="backtop">
    <div
      v-show="visible"
      @click="handleClick"
      :style="styles"
      class="bf-backtop">
      <slot>
        <bf-icon name="caretDoubleUp"></bf-icon>
      </slot>
    </div>
  </transition>
</template>

<script>

function throttle(fn, delay) {
  let timer = null
  return function (...args) {
    if (!timer) {
      timer = setTimeout(function () {
        fn.apply(this, args)
        clearTimeout(timer)
        timer = null
      }, delay)
    }
  }
}

const cubic = value => value ** 3;
const easeInOutCubic = value => value < 0.5
  ? cubic(value * 2) / 2
  : 1 - cubic((1 - value) * 2) / 2;

export default {
  name: 'Backtop',
  props: {
    visibilityHeight: {
      type: Number,
      default: 120
    },
    target: [String],
    right: {
      type: Number,
      default: 58
    },
    bottom: {
      type: Number,
      default: 58
    }
  },
  data() {
    return {
      el: null,
      visible: false,
      scrollHandler: () => void 0,
      scrollFlag: false
    };
  },
  computed: {
    styles() {
      const right = `${this.right}px`;
      const bottom = `${this.bottom}px`;
      return {
        right,
        bottom
      }
    }
  },
  mounted() {
    this.initScroll();
    this.scrollHandler = throttle(this.onScroll, 300);
    this.el && this.el.addEventListener('scroll', this.scrollHandler);
  },
  methods: {
    initScroll() {
      this.el = document.documentElement || document.body;
      if (this.target) {
        this.el = document.querySelector(this.target);
        if (!this.el) {
          throw new Error(`target is not existed: ${this.target}`);
        }
      }
    },
    onScroll() {
      const scrollTop = this.el.scrollTop;
      this.visible = scrollTop >= this.visibilityHeight;
    },
    handleClick(e) {
      this.scrollToTop();
      this.$emit('click', e);
    },
    scrollToTop() {
      let rAF = window.requestAnimationFrame;
      const el = this.el;
      const startTime = Date.now();
      const startTop = el.scrollTop;
      if (!rAF) {
        const genRAFTimer = (fn) => {
          const timer = setTimeout((...args) => {
            fn.apply(this, args);
            clearTimeout(timer);
          }, 16)
        }
        rAF = func => genRAFTimer(func)
      }
      const frameFunc = () => {
        if (!this.scrollFlag) {
          this.$emit('scrollStart');
          this.scrollFlag = true;
        }
        const progress = (Date.now() - startTime) / 500;
        this.$emit('scroll', progress)
        if (progress < 1) {
          el.scrollTop = startTop * (1 - easeInOutCubic(progress));
          rAF(frameFunc);
        } else {
          el.scrollTop = 0;
          this.$emit('scrollEnd');
          this.scrollFlag = false;
        }
      };
      
      rAF(frameFunc);
    }
  },

  beforeDestroy() {
    this.el.removeEventListener('scroll', this.scrollHandler);
  }
};
</script>
